const EOF = Symbol("EOF")
const css = require('css')
let currentToken = null;
let currentAttribute = null;
let curretTextNode = null;
let stack = [{type : "document", children : []}]
let rules = []


function specificity(selector){
    var p = [0, 0, 0, 0]
    var selectorParts = selector.split(" ")
    for(var part of selectorParts){
        if(part.charAt(0) === "#"){
            p[1] += 1
        }else if(part.charAt(0) === "."){
            p[2] += 1
        }else {
            p[3] += 1
        }
    }
    return p
}

function compaire(p1,p2){
    if(p1[0] - p2[0]){
        return p1[0] - p2[0]
    }
    if(p1[1] - p2[1]){
        return p1[1] - p2[1]
    }
    if(p1[2] - p2[2]){
        return p1[2] - p2[2]
    }
    return p1[3] - p2[3]
}

function match(element, selector){
    if(!selector || !element.attributes){
        return false
    }

    if(selector.charAt(0) === "#"){
        var attr = element.attributes.filter(attr => attr.name === "id")[0]
        if(attr && attr.value === selector.replace('#', '')){
            return true
        }
    }else if(selector.charAt(0) === "."){
        var attr = element.attributes.filter(attr => attr.name === "class")[0]
        if(attr && attr.value === selector.replace(".",'')){
            return true
        }
    }else {
        if(element.tagName === selector){
            return true;
        }
    }

    return false

}

function addCSSRules(text){
    var ast = css.parse(text)
    rules.push(...ast.stylesheet.rules)
}

function computeCSS(element){
    var elements = stack.slice().reverse()

    if(!element.computedStyle){
        element.computedStyle = {}
    }

    for(let rule of rules){
        var selectorPars = rule.selectors[0].split(" ").reverse()

        if(!match(element,selectorPars[0])){
            continue
        }

        let matched = false

        var j = 1
        for(var i = 0; i< elements.length; i++){
            if(match(elements[i],selectorPars[j])){
                j++
            }
        }

        if(j >= selectorPars.length){
            matched = true
        }

        if(matched){
            var sp = specificity(rule.selectors[0])
            var computedStyle = element.computedStyle
            for(var declaration of rule.declarations){
                if(!computedStyle[declaration.property]){
                    computedStyle[declaration.property] = {}
                }
                if(!computedStyle[declaration.property].specificity){
                    computedStyle[declaration.property].value = declaration.value
                    computedStyle[declaration.property].specificity = sp
                }else if(compaire(computedStyle[declaration.property].specificity,sp) < 0){
                    computedStyle[declaration.property].value = declaration.value
                    computedStyle[declaration.property].specificity = sp
                }
                
            }

        }
    }

}


function emit(token){
    
    let top = stack[stack.length - 1]

    if(token.type === "startTag"){
        let element = {
            type : "element",
            children : [],
            attributes : []
        }

        element.tagName = token.tagName
        for(let p in token){
            if(p != "type" && p != 'tagName'){
                element.attributes.push({
                    name : p,
                    value: token[p]
                })
            }
        }

        computeCSS(element)

        top.children.push(element)
        //element.parent = top

        if(!token.isSelfClosing){
            stack.push(element)
        }
        curretTextNode = null;

    }else if(token.type === "endTag"){
        if(top.tagName != token.tagName){
            throw new Error("Tag start end doesn't match!")
        } else {
            // 遇到style标签，执行添加css操作
            if(top.tagName === "style"){
                addCSSRules(top.children[0].content)
            }
            stack.pop()
        }
        curretTextNode = null
    }else if(token.type === "text"){
        if(curretTextNode === null){
            curretTextNode = {
                type : 'text',
                content : ""
            }
            top.children.push(curretTextNode)
        }
        curretTextNode.content += token.content
    }
}

function data(c){
    if(c === '<'){
        return tagOpen;
    }else if(c === EOF){
        emit({
            type:"EOF"
        })
        return;
    }else{
        emit({
            type:"text",
            content : c
        });
        return data;
    }

}

function tagOpen(c){
    if(c === '/'){
        // 代表遇到结束标签
        return endTagOpen;
    }else if(c.match(/^[a-zA-Z]$/)){
        currentToken = {
            type : "startTag",
            tagName : ""
        }
        // 标签正文
        return tagName(c);
    }else{
        return ;
    }
}

function endTagOpen(c){
    if(c.match(/^[a-zA-Z]$/)){
        currentToken = {
            type : "endTag",
            tagName : ""
        }
        return tagName(c);
    }else if(c === '/'){
        
    }else if(c === '>'){

    }else{}
}

function tagName(c){
    if(c.match(/^[\t\n\f ]$/)){
        return beforeAttributeName;
    }else if(c === "/"){
        return selfClosingStateTag;
    }else if(c.match(/^[a-zA-Z]$/)){
        currentToken.tagName += c
        return tagName;
    }else if(c === ">"){
        emit(currentToken)
        return data;
    }else{
        return tagName;
    }
}

// 开始处理属性
function beforeAttributeName(c){
    if(c.match(/^[\t\n\f ]$/)){
        return beforeAttributeName;
    }else if(c === ">" || c === "/" || c === EOF){
        return afterAttributeName(c);
    }else if(c === "="){
        
    }else {
        currentAttribute = {
            name : "",
            value : ""
        }
        return attributeName(c)
    }
}

function attributeName(c){
    if(c.match(/^[\t\n\f ]$/) || c === "/" || c === ">" || c === EOF){
        return afterAttributeName(c);
    }else if(c === "="){
        return beforeAttributeValue
    }else if(c === "\u0000"){

    }else if(c === "\"" || c === "'" || c === "<"){

    }else{
        currentAttribute.name += c
        return attributeName;
    }
}

function afterAttributeName(c){
    if(c.match(/^[\t\n\f ]$/)){
        return afterAttributeName
    }else if(c === "/"){
        return selfClosingStateTag
    }else if(c === "="){
        return beforeAttributeValue
    }else if(c === ">"){
        currentToken[currentAttribute.name] = currentAttribute.value
        emit(currentToken);
        return data
    }else if(c === EOF){

    }else{
        currentToken[currentAttribute.name] = currentAttribute.value
        currentAttribute = {
            name : "",
            value : ""
        }
        return attributeName(c)
    }
}

function beforeAttributeValue(c){
    if(c.match(/^[\t\n\f ]$/) || c === "/" || c === ">" || c === EOF){
        return beforeAttributeValue;
    }else if(c === "\""){
        return doubleQutedAttributeValue
    }else if(c === "'"){
        return singleQutedAttributeValue
    }else if(c === ">"){

    }else {
        return unQutedAttributeValue(c)
    }
}

function singleQutedAttributeValue(c){
    if(c === "'"){
        currentToken[currentAttribute.name] = currentAttribute.value;
        return afterQutedAttributeValue
    }else if(c === "\u0000"){
        
    }else if(c === EOF){

    }else{
        currentAttribute.value += c
        return doubleQutedAttributeValue
    }
}

function doubleQutedAttributeValue(c){
    if(c === "\""){
        currentToken[currentAttribute.name] = currentAttribute.value;
        return afterQutedAttributeValue
    }else if(c === "\u0000"){

    }else if(c === EOF){
    
    }else{
        currentAttribute.value += c
        return doubleQutedAttributeValue
    }
}

function unQutedAttributeValue(c){
    if(c.match(/^[\t\n\f ]$/)){
        currentToken[currentAttribute.name] = currentAttribute.value;
        return beforeAttributeName
    }else if(c === "/"){
        currentToken[currentAttribute.name] = currentAttribute.value;
        return selfClosingStateTag
    }else if(c === ">"){
        currentToken[currentAttribute.name] = currentAttribute.value;
        emit(currentToken);
        return data
    }else if(c === "\u0000"){
    
    }else if(c === "\"" || c === "'" || c === "<" || c === "=" || c ==="`"){

    }else if(c === EOF){

    }else{
        currentAttribute.value += c
        return unQutedAttributeValue
    }
}

function afterQutedAttributeValue(c){
    if(c === "/"){
        return selfClosingStateTag
    }else if(c.match(/^[\t\n\f ]$/)){
        return beforeAttributeName
    }else if(c === ">"){
        emit(currentToken);
        return data
    }else{

    }
}


function selfClosingStateTag(c){
    if(c === ">"){
        currentToken.isSelfClosing = true
        emit(currentToken)
        return data;
    }else if(c === "EOF"){
        
    }else{
        
    }
}

module.exports.parserHTML = function parserHTML(html){
    let state = data;
    for(let c of html){
        state = state(c)
    }
    state = state(EOF)
    return stack[0]
}